//
//  Lattice.cpp
//  phi_four
//
//  Created by Snir Gazit on 3/8/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#include "Lattice.h"

void Lattice::initSquare(boost::array<int, DIM> const &latSize) {
	cordinationNumber_ = 2 * DIM;
	siteLattice_.init(latSize);
	bondLattice_.init(latSize);
	latSize_ = latSize;
	SiteIterIndex iter = siteLattice_.beginIndex();
	for (; iter != siteLattice_.endIndex(); iter++) {
		IndexType currentPos = iter.getIndex();
		iter->siteIndex_ = currentPos;
		bondLattice_(currentPos).resize(DIM);
		iter->totalBonds_ = 0;
		for (int d = 0; d < DIM; d++) {
			Bond& currentbond = bondLattice_(currentPos)[d];
			currentbond.bondVal[LEFT] = currentbond.bondVal[RIGHT] = 0;
			currentbond.dir = d;
			boost::array<int, DIM> nPos = currentPos;
			if (nPos[d] == (latSize_[d] - 1)) {
				nPos[d] = 0;
			} else
				nPos[d]++;
			SiteIter nIter = siteLattice_.iterByPos(nPos);
			iter->bondArray_.push_back(
					BondData(&currentbond, nIter, RIGHT, CREATE));
			iter->bondArray_.push_back(
					BondData(&currentbond, nIter, LEFT, REMOVE));
			nIter->bondArray_.push_back(
					BondData(&currentbond, iter.getIter(), LEFT, CREATE));
			nIter->bondArray_.push_back(
					BondData(&currentbond, iter.getIter(), RIGHT, REMOVE));
		}
	}
//    for (; iter != siteLattice_.endIndex(); iter++) {
//        std::sort(iter->bondArray_.begin(), iter->bondArray_.end(),
//                  [](BondData bond1,BondData bond2)->bool
//        {
//            if (bond1.dir>bond2.dir) return true;
//            else
//                if (bond.moveDir==LEFT return true
//                    else return false;
//        })
//    }
}

void Lattice::init(boost::array<int, DIM> const &latSize) {
	this->initSquare(latSize);
	ira_ = siteLattice_.begin();
	masha_ = siteLattice_.begin();
//	ira_->totalBonds_ = 1;
//	string outPath = "/home/snirgaz";
//	this->dump(outPath);
//	this->read(outPath);
}

bool Lattice::isClosed() const {
	return ira_ == masha_;
}
Lattice::SiteIter Lattice::getMasha() {
	return masha_;
}
Lattice::SiteIter Lattice::getIra() {
	return ira_;
}
void Lattice::setMasha(Lattice::SiteIter &iter) {
	masha_ = iter;
}
void Lattice::setIra(Lattice::SiteIter &iter) {
	ira_ = iter;
}
int Lattice::getCordinationNumber() const {
	return cordinationNumber_;
}

int Lattice::getNumOfSites() const {
	return siteLattice_.totalSize();
}

Lattice::SiteIter Lattice::getSite(int distanceFromStart) {
	return siteLattice_.begin() + distanceFromStart;
}
boost::array<int, DIM> Lattice::getSizes() {
	return latSize_;
}

bool Lattice::testConfiguration(int &totalBonds) {
	totalBonds = 0;
	SiteIter it = siteLattice_.begin();
	for (; it != siteLattice_.end(); it++) {
		int count = 0;
		Site::BondArray::iterator itb = it->bondArray_.begin();
		for (; itb != it->bondArray_.end(); itb++) {
			if (itb->createRemove_ == REMOVE)
				continue;
			if (itb->moveDir_ == RIGHT) {
				count += itb->bond_->bondVal[0];
				assert(itb->bond_->bondVal[0] >= 0);
				count -= itb->bond_->bondVal[1];
				assert(itb->bond_->bondVal[1] >= 0);
			} else {
				count -= itb->bond_->bondVal[0];
				assert(itb->bond_->bondVal[0] >= 0);
				count += itb->bond_->bondVal[1];
				assert(itb->bond_->bondVal[1] >= 0);
			}
			totalBonds += itb->bond_->bondVal[0];
		}
#ifdef SIM_ALG
		if (it == this->getIra())
			count--;
		if (it == this->getMasha())
			count++;
#endif
		if (count != 0) {
			return false;
		}
	}
	return true;
}
Lattice::BondArrayIter Lattice::BondBegin() {
	return bondLattice_.begin();
}
Lattice::BondArrayIter Lattice::BondEnd() {
	return bondLattice_.end();
}
Lattice::SiteIter Lattice::siteBegin() {
	return siteLattice_.begin();
}
Lattice::SiteIter Lattice::siteEnd() {
	return siteLattice_.end();
}
int Lattice::getTotalBonds() const {
	return totalBonds_;
}
void Lattice::setToalBonds(int totalBonds) {
	totalBonds_ = totalBonds;
}
void Lattice::setNormRatio(double normRatio) {
	normRatio_ = normRatio;
}
double Lattice::getNormRatio() const {
	return normRatio_;
}

void Lattice::dump(string fileName) {
	try {
		bool testConf = testConfiguration(totalBonds_);
		if (!testConf)
			throw "Bad Configuration Dumping";
	} catch (string &e) {
		cout << e;
		exit(1);
	}
	fileName += "/Lat.dat.zip";
	namespace io = boost::iostreams;
	namespace ar = boost::archive;
	ofstream file(fileName.c_str(),
			std::ios::out | std::ios::binary | std::ios::trunc);
	io::filtering_ostream out;
	out.push(io::zlib_compressor());
	out.push(file);
	ar::text_oarchive oa(out);
//	ofstream file(fileName.c_str(), ios_base::out);
//	ar::text_oaromchive oa(file);
	oa << *this;

}
void Lattice::read(string fileName) {
	fileName += "/Lat.dat.zip";
	namespace io = boost::iostreams;
	namespace ar = boost::archive;
	ifstream file(fileName.c_str(), ios_base::in | ios_base::binary);
	io::filtering_istream in;
	in.push(io::zlib_decompressor());
	in.push(file);
	ar::text_iarchive oi(in);
	oi >> *this;
}
